/**
 * File PercentOrAbsoluteInteger.java
 * ---------------------------------------------------------
 *
 * Copyright (C) 2012 David Bauske (david.bauske@googlemail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * - The above copyright notice and this permission notice shall be included in all copies or substantial portions of
 * the Software.
 * - The origin of the software must not be misrepresented.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 * FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * TL;DR: As long as you clearly give me credit for this software, you are free to use as you like, even in commercial
 * software, but don't blame me if it breaks something.
 */

package net.cl.util.math;

import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.cl.util.values.InputFormatException;

/**
 * Represents a numeric value that was either a percentage or an absolute value.
 * 
 * It's constructor recieves the input string which is parsed and then converted to the respective value.
 * The measning is determined by the trailing string:
 * 
 * If the string ends with a percent sign (%), the value is interpreted as a percentage. Otherwise (if not specified
 * differently), the value is treated as an absolute value.
 * 
 * @author David Bauske
 */
public final class PercentOrAbsoluteInteger
{
	private static final Pattern	FORMAT	= Pattern.compile("[+-]?\\d+\\D*");
	private static final Pattern	NUMBER	= Pattern.compile("[+-]?\\d+");

	private boolean	mPercentage;
	private int		mValue;

	public static PercentOrAbsoluteInteger valueOf(String pInput) throws InputFormatException
	{
		return valueOf(pInput, null);
	}

	/**
	 * Constructs a new PercentOrAbsoluteInteger from a given string, but restricts the possible trailing strings to
	 * those
	 * in the given set.
	 *
	 * @param pInput The input string. Has the format <tt>&lt;number&gt;&lt;suffix&gt;</tt>. Must not be null.
	 * @param pAcceptedEnds A set of accepted suffixes (in addition to the obligatory percent sign %). Case
	 *            insensitive. May be null to allow all suffixes (including the empty string).
	 * @return The new instance of PercentOrAbsoluteInteger
	 * @throws InputFormatException if the input format is invalid (not an integer number, wrong suffix)
	 */
	public static PercentOrAbsoluteInteger valueOf(String pInput,
			Set<String> pAcceptedEnds) throws InputFormatException
	{
		if(pInput == null)
		{
			throw new NullPointerException("pInput may not be null.");
		}

		PercentOrAbsoluteInteger result = new PercentOrAbsoluteInteger();

		if(FORMAT.matcher(pInput).matches())
		{
			Matcher m = NUMBER.matcher(pInput);
			m.find();
			String number = m.group();
			String suffix = pInput.substring(number.length()).trim();

			result.mValue = Integer.parseInt(number);
			result.mPercentage = suffix.equals("%");

			if(pAcceptedEnds != null)
			{
				if(!pAcceptedEnds.contains(suffix.toLowerCase()))
				{
					throw new InputFormatException("Illegal suffix '" + suffix + "'.");
				}
			}
		}
		else
		{
			// Wrong input format
			throw new InputFormatException("Invalid input format.");
		}

		return result;
	}

	/**
	 * Use the valueOf() methods to create an instance.
	 */
	private PercentOrAbsoluteInteger()
	{

	}

	/**
	 * Applys the value of this object.
	 *
	 * If isPercentage() is true, the method returns pInput * percentage, otherwise the absolute value is returned.
	 *
	 * @param pInput The reference value
	 * @return The resulting value
	 */
	public int apply(int pInput)
	{
		if(mPercentage)
		{
			return (int) (pInput * (mValue / 100.0));
		}
		else
		{
			return mValue;
		}
	}

	/**
	 * @return the numerical value of the object
	 */
	public int getValue()
	{
		return mValue;
	}

	/**
	 * @return <i>true</i> if thie object is a percentage
	 */
	public boolean isPercentage()
	{
		return mPercentage;
	}
}
